package com.dynamic.mybatis.core.datasource;


import com.dynamic.mybatis.core.parser.NamespaceHelper;
import com.dynamic.mybatis.core.utils.BeanUtils;
import com.dynamic.mybatis.core.utils.StringUtils;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

public class DynamicMultipleDataSource extends AbstractRoutingDataSource {

    //dataSourceKey , dataSource
    private Map<Object, DataSource> dataSources;
    //dataSourceKey , attributeKey attributeValue
    private Map<Object, Map<Object, Object>> attributes;

    private Map<Object, DataSourceProperty> properties;


    public DynamicMultipleDataSource() {
        this(null, new HashMap<>());
    }

    public DynamicMultipleDataSource(DataSource defaultDataSource, Map<Object, DataSource> dataSources) {
        super.setDefaultTargetDataSource(defaultDataSource);
        super.setTargetDataSources((Map<Object, Object>) (Object) dataSources);
        super.afterPropertiesSet();
        this.attributes = new HashMap<>();
        this.properties = new HashMap<>();
        this.dataSources = dataSources;
    }

    public void set(DataSource defaultDataSource, Map<Object, DataSource> dataSources) {
        super.setDefaultTargetDataSource(defaultDataSource);
        super.setTargetDataSources((Map<Object, Object>) (Object) dataSources);
        super.afterPropertiesSet();
        this.attributes = new HashMap<>();
        this.properties = new HashMap<>();
        this.dataSources = dataSources;
    }

    public boolean hasDataSource(String dataSourceKey) {
        return this.dataSources.containsKey(dataSourceKey);
    }

    public void setMainDataSource(String dataSourceKey) {
        DataSource mainDataSource = null;
        for (Map.Entry<Object, DataSource> entry : this.dataSources.entrySet()) {
            if (Objects.equals(entry.getKey(), dataSourceKey)) {
                mainDataSource = entry.getValue();
            }
        }
        if (mainDataSource != null) {
            this.set(mainDataSource, this.dataSources);
        }
    }

    /**
     * 　* @Description: key 数据源key, vlaue 数据源
     *
     */
    public void set(Map<Object, DataSource> dataSources) {
        if (dataSources == null || dataSources.size() < 1) {
            return;
        }
        close(this.dataSources);
        this.dataSources = dataSources;
        super.setTargetDataSources((Map<Object, Object>) (Object) dataSources);
        super.afterPropertiesSet();
    }

    public Map<Object, DataSource> getDataSources() {
        return this.dataSources;
    }

    public DataSource getDataSource(Object dataSourceCode) {
        return this.dataSources.get(dataSourceCode);
    }

    public DataSourceProperty getDataSourceProperty(Object dataSourceCode) {
        return this.properties.get(dataSourceCode);
    }

    /**
     * 　* @Description: key 数据源key, value 数据源对应的扩展属性
     *
     */
    public void setAttributes(Map<Object, Map<Object, Object>> attributes) {
        this.attributes.putAll(attributes);
    }

    /**
     * 　* @Description: key 数据源key, value 数据源对应的扩展属性
     *
     */
    public void setAttributes(Object dtaSourceKey, Map<Object, Object> attributes) {
        if (!this.dataSources.containsKey(dtaSourceKey) || attributes == null) {
            return;
        }
        Map<Object, Object> attres = this.attributes.get(dtaSourceKey);
        if (attres == null) {
            attres = new HashMap<>();
            this.attributes.put(dtaSourceKey, attres);
        }
        attres.putAll(attributes);
    }

    /**
     * 　* @Description: key 数据源key, value 数据源对应的扩展属性
     *
     */
    public void setProperty(DataSourceProperty property) {
        String key = NamespaceHelper.getIdentity(property.getNamespace(), property.getCode());
        if (!this.dataSources.containsKey(key) || properties == null) {
            return;
        }
        if (StringUtils.isNotBlank(property.getUid())) {
            Map.Entry<Object, DataSourceProperty> existentEntry = this.properties.entrySet().stream().filter(entry -> Objects.equals(entry.getValue().getUid(), property.getUid())).findFirst().orElse(null);
            if (existentEntry != null) {
                String namespace = existentEntry.getValue().getNamespace();
                String code = existentEntry.getValue().getCode();
                BeanUtils.copy(property, existentEntry.getValue());
                existentEntry.getValue().setNamespace(namespace);
                existentEntry.getValue().setCode(code);
                this.properties.put(existentEntry.getKey(), property);
            }
        } else {
            property.setUid(UUID.randomUUID().toString());
        }
        this.properties.put(NamespaceHelper.getIdentity(property.getNamespace(), property.getCode()), property);
    }


    /**
     * 　* 获取数据源附加属性
     *
     */
    public Map<Object, Object> getAttributes(Object key) {
        return this.attributes.get(key);
    }

    public DataSourceProperty getProperty(Object key) {
        return this.properties.get(key);
    }

    public void put(Object key, DataSource dataSource) {
        if (dataSources == null || dataSources.size() < 1) {
            this.dataSources = new HashMap<>();
        }
        this.dataSources.put(key, dataSource);
        set(this.dataSources);
    }

    public void remove(Object key) {
        if (dataSources == null || dataSources.size() < 1) {
            return;
        }
        close(key);
        this.dataSources.remove(key);
        set(this.dataSources);
        this.attributes.remove(key);
    }

    private void close(Map<Object, DataSource> dataSources) {
        //被重置数据源和重置数据源属于同一对象时，不进行关闭
        if (dataSources == null || dataSources.size() < 1 || (this.dataSources != null && this.dataSources == dataSources)) {
            return;
        }
        dataSources.forEach((key, value) -> {
            DataSource dataSource1 = this.resolveSpecifiedDataSource(dataSources.get(key));
            DataSource dataSource2 = this.resolveSpecifiedDataSource(this.dataSources.get(key));
            if (dataSource1 != null && dataSource2 != null && dataSource1 != dataSource2) {
                close(key);
            }
        });
    }

    private void close(Object key) {
        DataSource dataSource = this.resolveSpecifiedDataSource(this.dataSources.get(key));
        if (dataSource != null && dataSource instanceof Closeable) {
            try {
                ((Closeable) dataSource).close();
            } catch (IOException e) {
                throw new RuntimeException("dataSource close is error." + e, e);
            }
        }
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.get();
    }


}
